/*->c.dir */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>

#include "h.os"
#include "h.wimp"
#include "h.bbc"
#include "h.flex"
#include "h.font"

#include "h.def"
#include "h.wos"
#include "h.main"
#include "h.mym"
#include "h.serial"
#include "h.term"
#include "h.ram"
#include "h.file"
#include "h.pr"
#include "h.key"
#include "h.trans"

#include "h.vtkey"

#include "h.script"

#include "h.ftp"


#include "h.dir"




/*****************************************************************************/

int   band;    
char  sysname[CURSYSNAMELEN];

char  prefixstring[PREFIXLEN];
char  passwordstring[PASSWLEN];

int   passcrc=0;
int   unlocked=0;



/*****************************************************************************/

                        /* step between each macro entry in window */
#define TDIRDY   56
                        /* step between top of window and first macro */
#define TDIRFY   12
                        /* height of macro definitions */
#define TDIRHY   48
                        /* x step between entries */
#define TDIRDX   8
#define TDIRPX   8


#define TDIRFW   13
#define TDIRSW   24
#define TDIRTW   12
#define TDIRGW   14
#define TDIRHW   3


#define TDIRFXW   TDIRFW*16+2*TDIRPX
#define TDIRSXW   TDIRSW*16+2*TDIRPX
#define TDIRTXW   TDIRTW*16+2*TDIRPX
#define TDIRGXW   TDIRGW*16+2*TDIRPX
#define TDIRHXW   TDIRHW*16+2*TDIRPX


#define TDIRFX    8
#define TDIRSX    TDIRFXW+TDIRFX+TDIRDX
#define TDIRTX    TDIRSXW+TDIRSX+TDIRDX
#define TDIRGX    TDIRTXW+TDIRTX+TDIRDX
#define TDIRHX    TDIRGXW+TDIRGX+TDIRDX
#define TDIRWIDTH TDIRHXW+TDIRHX+TDIRDX



#define TDNAME     1
#define TDSHOW     2
#define TDNUMBER   5
#define TDPRE      6
#define TDNOPW     8
#define TDPREFIX   10

#define TDPRESB1   13
#define TDPRESI1   14
#define TDPRES1    15

#define TDPRESB2   16
#define TDPRESI2   17
#define TDPRES2    18

#define TDCONFIG    7
#define TDTERM     20
#define TDRATE     21
#define TDBITS     22
#define TDBAND     23
#define TDPROMPT1  25
#define TDPROMPT2  29
#define TDPROMPT3  33
#define TDREPLY1   27
#define TDREPLY2   31
#define TDREPLY3   35

#define TDPOSTSB1  37
#define TDPOSTSI1  38
#define TDPOSTS1   39

#define TDPOSTSB2  40
#define TDPOSTSI2  41
#define TDPOSTS2   42

#define TDCOMMENT  44
#define TDOK       45
#define TDCANCEL   11
#define TDGRIGHT   9




typedef struct
{
 char name[24];
 char  mark[8];
 char copy[64];
 int  passcrc;
 char prefix[PREFIXLEN];

 char stuff[160];
} dheader;










typedef struct fixedbit
{
 char name[SYSNAMELEN];
 char number[NUMLEN];

 int  txrate;
 int  rxrate;

 unsigned int data:4;
 unsigned int parity:4;
 unsigned int stop:4;
 unsigned int band:8;
 unsigned int term:8;

 unsigned int  config:1;
 unsigned int  show:1;            /* show on menu */
 unsigned int  nopw:1;            /* password needed to dial */
 unsigned int  prefix:1;          /* use prefix ? */
 unsigned int  selected:2;
 unsigned int  lastcall:1;
 unsigned int  call:1;

} fixedbit;



typedef struct entry 
{
 fixedbit fix;

 char     script[4][128];             
 char     prompt[3][32];
 char     response[3][32];
 char     comment[36];
 char     oldname[SYSNAMELEN];
 char     predial[32];

 int      handle;
 int      inuse;

 char     scriptlabel[4][16];

} entry;



typedef struct packedentry 
{
 fixedbit fix;

 int      size;                        /* size of string entry   */
 int      offset;                      /* offset of string entry */
} packedentry;



static packedentry * dir;
static char        * dirs;        /* directory string space */


static int dirtot;                /* total number of directory entries */
static int dirmodded;


#define MAXEW 2


int    entrywindows;

static entry ewind[MAXEW];

static int    sentry;


/*****************************************************************************/


static void lockpackedentry(int n,int lock)
{
 int    offset;
 char * p;
 int    i;
 int    len=strlen(passwordstring);
 int    size;

 if(passcrc)
 {
  offset=dir[n].offset;
  size=dir[n].size;
  p=dirs+offset;
  i=0;

  while(size--)
  {
   *p=*p ^ passwordstring[i];
   *p=*p ^ 0xCC;
   p++;
   if(++i>=len) i=0;
  }
 }
 lock=0;
}



/* take fixed length prefix string, and make it unreadable */


static int rotate(int byte,int shift)
{
 if(shift<0) shift=8+shift;

 byte=(byte<<shift);
 byte=(byte+(byte>>8)) & 0xFF;
 return(byte ^ 0xFF);
}


void scrunch(int doit)
{
 char temp[PREFIXLEN];
 int  i;
 int  j;
 int  sum;

 if(doit)
 {
  for(i=0;i<PREFIXLEN;i++)
  {
   sum=0;
   for(j=0;j<8;j++) sum+=prefixstring[(i+j) % PREFIXLEN] & (1<<j);
   temp[i]=rotate(sum,i % 8);
  }
 }
 else
 {
  for(i=0;i<PREFIXLEN;i++) prefixstring[i]=rotate(prefixstring[i],-(i % 8));

  for(i=0;i<PREFIXLEN;i++)
  {
   sum=0;
   for(j=0;j<8;j++)
   {
    if((i-j)<0)  sum+=prefixstring[PREFIXLEN+(i-j)] & (1<<j);
    else         sum+=prefixstring[(i-j)] & (1<<j);
   }
   temp[i]=sum;
  }
 }

 memcpy(prefixstring,temp,PREFIXLEN);
}




int passwordcrc(char * pw)
{
 int crc;
 crc=0;

 while(*pw)
 {
  if(*pw!=13) crc=updcrc((*pw++),crc);
  else        pw++;
 }
 return(crc);
}


int passflag;


void passwordicon(void)
{
 if(icon==2 || icon==3)
 {
  passflag=icon-2;
 }
}



void passwordkey(int * key)
{
 if(*key==27) passflag=-1;
 else
 if(*key==13) passflag=1;
 else
 return;
 *key=-1;
}



/* returns 1==OK 0==NO -1==FORGET IT */

int getpassword(void)
{
 char buff[PASSWLEN];
 char * p;
 char * q;
 char * pw;

 if(unlocked) return(1);
 else
 if(passcrc==0)
 {
  unlocked=1;
  return(1);
 }

 if(!createwindow(PASSWORD)) return(-1);
 passflag=-2;

 menuwindow(whandle[PASSWORD]);

 while(passflag==-2)
 {
  getw(whandle[PASSWORD]);
  if(!(wflags & 0x10000))
  {
   passflag=-1;
   break;
  }
  poll(0);
 }

 p=pw=iconaddr(whandle[PASSWORD],0);
 q=buff;

 while((*q++=tolower(*p++))!=0);
 *q=0;
 p=buff;

 if(passflag==1)       /* attempt at saying OK */
 {
  if(passwordcrc(p)!=passcrc) passflag=0;
  else                        
  {
   q=passwordstring;
   do
   {
    if(*p!=13) *q++=*p;
   } while(*p++);

   unlocked=1;
  }
 }

 memset(pw,0,PASSWLEN);

 zapmenu();
 closedownt(PASSWORD);

 return(passflag);
}



void changepassword(char * newpassword)
{
 int    i;
 char * p;
 int    c;

 for(i=0;i<dirtot;i++)
  if(!dir[i].fix.nopw) lockpackedentry(i,0);

 strcpy(passwordstring,newpassword);
 p=passwordstring;
 while((c=*p)!=0) *p++=tolower(c);
 passcrc=passwordcrc(passwordstring);

 for(i=0;i<dirtot;i++)
  if(!dir[i].fix.nopw) lockpackedentry(i,1);
}





int bitratemap[MAXRATES]=
         {50,75,110,134,150,300,600,1200,1800,2400,3600,4800,7200,9600,19200,
          38400,57600,115200};

char * bitratemaps[MAXRATES]=
         {"50","75","110","134","150","300","600","1200","1800","2400",
          "3600","4800","7200","9600","19200","38400","57600","115200"};




/* nb needed by status menus */

int bitrate2int(int bitrate)
{
 int i;
 for(i=0;i<MAXRATES;i++)
  if(bitratemap[i]==bitrate) return(i);
 return(0);
}



static char scriptmap[4]={TDPRES1,TDPRES2,TDPOSTS1,TDPOSTS2};


static char scripticons[4][3]=
{
 TDPRES1,TDPRESI1,TDPRESB1,
 TDPRES2,TDPRESI2,TDPRESB2,
 TDPOSTS1,TDPOSTSI1,TDPOSTSB1,
 TDPOSTS2,TDPOSTSI2,TDPOSTSB2
};



int scriptfromicon(int icon)
{
 int i;
 int j;

 for(i=0;i<4;i++)
  for(j=0;j<3;j++)
   if(scripticons[i][j]==icon) return(i);

 return(-1);
}




static int findname(char * name)
{
 int i;
 for(i=0;i<dirtot;i++)
 {
  if(!cstrcmp(name,dir[i].fix.name)) return(i);
 }
 return(-1);
}






static int createentry(void)
{
 int i;
 i=dirtot++;
 if(flex_extende((flex_ptr)&dir,dirtot*sizeof(packedentry)))
 {
  dir[i].size=0;
  dir[i].offset=i?dir[i-1].offset+dir[i-1].size:0;

/* dprintf(1,"dirtot=%d i=%d offset=%d",dirtot,i,dir[i-1].offset); */


  return(i);
 }
 else
  return(-1);
}



static void dirextent(void)
{
 int sety;
 int n;
 if(dirtot<3) n=3;
 else         n=dirtot;
 sety=TDIRFY+TDIRDY*n+TDIRFY;
 extent(whandle[TDIR],0,-sety,TDIRWIDTH+TDIRDX,0);
}



static void dirtoentry(int en,int i)
{
 char *  p;
 int     j;
 entry * ep;

 ep=&ewind[en];

 ep->fix=dir[i].fix;

 strcpy(ep->oldname,dir[i].fix.name);

 if(!ep->fix.nopw) lockpackedentry(i,0);

 p=dirs+dir[i].offset;

 for(j=0;j<3;j++)
 {
  strcpy(ep->prompt[j],p);
  p+=strlen(p)+1;
 }


 for(j=0;j<3;j++)
 {
  strcpy(ep->response[j],p);
  p+=strlen(p)+1;
 }


 for(j=0;j<4;j++)
 {
  strcpy(ep->script[j],p);
  p+=strlen(p)+1;
 }

 strcpy(ep->comment,p);
 p+=strlen(p)+1;
 strcpy(ep->predial,p);

 if(!ep->fix.nopw) lockpackedentry(i,0);
}



static int setdirnstring(int i,int size)
{
 int offset;
 int oldsize;
 int delta;
 int j;
 
 offset=dir[i].offset;
 oldsize=dir[i].size;
 delta=size-oldsize;

 if(flex_midextende((flex_ptr)&dirs,offset+oldsize,delta))
 {
  for(j=0;j<dirtot;j++) if(dir[j].offset>=offset && i!=j) dir[j].offset+=delta;
  dir[i].size=size;
  return(1);
 }
 else
  return(0);
}



static void entrytodir(int i,int en)
{
 char buff[1024];
 char * p;
 int    len;
 int    tot;
 int    j;
 entry * ep;

 ep=&ewind[en];

 dir[i].fix=ep->fix;

 p=buff;
 tot=0;

 for(j=0;j<3;j++)
 {
  strcpy(p,ep->prompt[j]);
  len=strlen(p)+1;
  p+=len;
  tot+=len;
 }


 for(j=0;j<3;j++)
 {
  strcpy(p,ep->response[j]);
  len=strlen(p)+1;
  p+=len;
  tot+=len;
 }


 for(j=0;j<4;j++)
 {
  strcpy(p,ep->script[j]);
  len=strlen(p)+1;
  p+=len;
  tot+=len;
 }

 strcpy(p,ep->comment);
 len=strlen(p)+1;
 p+=len;
 tot+=len;

 strcpy(p,ep->predial);
 len=strlen(p)+1;
 p+=len;
 tot+=len;

 if(tot & 0x7) tot+=8-(tot & 0x7);

 if(setdirnstring(i,tot))
 {
  p=dirs+dir[i].offset;
  memcpy(p,buff,tot);
  if(!ep->fix.nopw) lockpackedentry(i,1);
 }
}



static void moddir(void)
{
 dirmodded=1;
}



/* deletes entry i from directory */

static void deleteentry(int i)
{
 setdirnstring(i,0);
 flex_midextend((flex_ptr)&dir,(i+1)*sizeof(packedentry),-sizeof(packedentry));
 dirtot--;
 moddir();
}




static int addentry(int en)
{
 int i;

 i=findname(ewind[en].fix.name);

 if(i<0) i=createentry();

 if(i>=0)
 {
  entrytodir(i,en);
  dirextent();
  refreshdirentry(i);
  return(1);
 }
 else
  return(0);
}





/* returns number of entries selected, and number of first */

static int selected(int * k)
{
 int i;
 int n;

 n=0;

 for(i=0;i<dirtot;i++)
 {
  if(dir[i].fix.selected)
  {
   if(!n) *k=i;
   n++;
  }
 }

 return(n);
}



/* see if there is an dir entry with the same name as the name
   in entry struct ci */

static int dduplicate(int ci)
{
 int i;

 for(i=0;i<dirtot;i++)
 {
  if(!cstrcmp(dir[i].fix.name,ewind[ci].fix.name)) return(i);
 }

 return(-1);
}



void savedir(void)
{
 FILE *  fp;
 int     size;
 dheader dh;

 memset(&dh,0,sizeof(dheader));
 strcpy(dh.name,"Hearsay Tele-Dir");
 strcpy(dh.mark,"0.00");
 strcpy(dh.copy,"(18-Jun-1991)");
 dh.passcrc=passcrc;
 memcpy(dh.prefix,prefixstring,PREFIXLEN);

 size=flex_size((flex_ptr)&dirs);

 fp=fopen(path(TDIRP),"wb");
 if(fp)
 {
  fwrite(&dh,1,sizeof(dheader),fp);

  fwrite(&dirtot,1,sizeof(int),fp);
  fwrite(&size,1,sizeof(int),fp); 
  fwrite(dir,dirtot,sizeof(packedentry),fp);
  fwrite(dirs,1,size,fp);
  fclose(fp);
  setftype(path(TDIRP),TELEDIR);
  dirmodded=0;
 }
}



void loaddir(void)
{
 FILE  * fp;
 int     i;
 int     size;
 dheader dh;

 fp=fopen(path(TDIRP),"rb");
 if(fp)
 {
  fread(&dh,1,sizeof(dheader),fp);

  passcrc=dh.passcrc;
  memcpy(prefixstring,dh.prefix,PREFIXLEN);

  fread(&dirtot,1,sizeof(int),fp);
  fread(&size,1,sizeof(int),fp);

  if(flex_extend((flex_ptr)&dir,dirtot*sizeof(packedentry)) &&
     flex_extend((flex_ptr)&dirs,size))
  {
   fread(dir,dirtot,sizeof(packedentry),fp);
   fread(dirs,1,size,fp);
   fclose(fp);
   dirmodded=0;
   for(i=0;i<dirtot;i++) dir[i].fix.selected=0;
  }
  else fatalerror("{TDIR00}");
 }
 else
 {
  *prefixstring=0;
  scrunch(1);
 }
}





/*****************************************************************************/

/* maps a window handle into an entry number */
/* placed in sentry */

int getentry(int handle)
{
 int i;

 for(i=0;i<MAXEW;i++)
 {
  if(ewind[i].inuse && ewind[i].handle==handle)
  {
   sentry=i;
   return(1);
  }
 }

 return(0);
}


/* gets a new entry handle, return handle or -1 if fails */

static int getentryhandle(void)
{
 int i;
 for(i=0;i<MAXEW;i++) if(!ewind[i].inuse) return(i);
 return(-1);
}


/* trashes an entry */

static void loseentryhandle(int i)
{
 ewind[i].inuse=0;
 if(ewind[i].handle)
 {
  wimp_close_wind(ewind[i].handle);
  wimp_delete_wind(ewind[i].handle);
 }
 ewind[i].handle=0;
}


/* close an entry window */

static void closeentry(int i,int ok)
{
 if(ok)
 {
  if(strcmp(ewind[i].fix.name,ewind[i].oldname))
  {                                               /* the name has changed */
   if(dduplicate(i)>=0)
   {
    if(confirm(CONDC,"{TDIR01}")!=1) ok=0;
   }
  }

  if(ok)
  {
   if(addentry(i)) moddir();
  }
 }

 loseentryhandle(i);
}

/*****************************************************************************/


void entryclosesub(int i)
{
 closeentry(i,0);
}



void entryclose(void)
{
 entryclosesub(sentry);
}


/*****************************************************************************/

static menuentry;

char * bandname[MAXBAND+1]=
{
 "",
 "L",
 "a",
 "b1",
 "b",
 "m"
};



static void writestatus(int i)
{
 entry * ep=&ewind[i];
 int handle=ewind[i].handle;

 writeicon(handle,TDTERM,transtoken(termname[ep->fix.term]));
 writeiconf(handle,TDRATE,"%d/%d",ep->fix.txrate,ep->fix.rxrate);
 writeiconf(handle,TDBITS,"%d%c%d",ep->fix.data,"NEOMS"[ep->fix.parity],
                                                            ep->fix.stop);

 writeicon(handle,TDBAND,bandname[ep->fix.band]);
}





void tdstatus(int txrate,int rxrate,int data,int stop,int parity)
{
 entry * ep=&ewind[menuentry];
 ep->fix.txrate=txrate;
 ep->fix.rxrate=rxrate;
 ep->fix.data=data;
 ep->fix.stop=stop;
 ep->fix.parity=parity;
 writestatus(menuentry);
}




static void setpopentry(int i)
{
 int j;
 int k;
 entry * ep=&ewind[i];

 menuentry=i;

 for(j=0;j<=TERMMAX;j++)  tickst(term_menu,j,j==ep->fix.term);
 for(j=0;j<=MAXBAND;j++)  tickst(callrate_menu,j,j==ep->fix.band);

 mwpoint(entry_menu,1,
         dynamsettingsz(ep->fix.txrate,ep->fix.rxrate,ep->fix.data,
                                       ep->fix.parity,ep->fix.stop,1));

 k=0;
 for(j=0;j<4;j++)
  if(ep->script[j][0])
  {
   sprintf(menuaddr(tdirscript_menu,k),"'%s'",leaf(ep->script[j]));
   k++;
  }

 for(j=k;j<4;j++)
  strcpy(menuaddr(tdirscript_menu,j),"''");
}


void decodeentry(int m2,int m3,int m4,int m5)
{
 entry * ep=&ewind[menuentry];
 int     i;
 int     k;

 switch(m2)
 {
  case 0: /* terminal */
         ep->fix.term=m3;
         writestatus(menuentry);
         break;

  case 1: /* quick setup */
         break;

  case 2: /* call rate */  
         if(m3>=0 && m3<6) ep->fix.band=m3;
         writestatus(menuentry);
         break;

  case 3: /* delete scripts */
         k=0;
         for(i=0;i<4;i++)
         {
          if(ep->script[i][0])
          {
           if(k==m3)
           {
            ep->script[i][0]=0;
            writeicon(ewind[menuentry].handle,scriptmap[i],"");
           }
           k++;
          }
         }
         break;
 }

 setpopentry(menuentry);
 m3=m4=m5;
}



static void entrypop(int i)
{
 setpopentry(i);
 popmenu(entry_menu);
}


/****************************************************************************/



int entryloadsub(char * filename,int i)
{
 entry * data=&ewind[i];
 int     n=scriptfromicon(micon);

 if(n>=0)
 {
  strcpy(data->script[n],filename);
  writeicon(ewind[i].handle,scriptmap[n],leaf(filename));
  return(1);
 }
 else
  return(NOLOAD);
}


int entryload(char * filename)
{
 return(entryloadsub(filename,sentry));
}



void entryiconsub(int i)
{
 entry * data=&ewind[i];
 int     handle=ewind[i].handle;


 switch(icon)
 {

  case TDGRIGHT:
                entrypop(i);
                break;

    case TDSHOW:
                selectst(handle,TDSHOW,data->fix.show^=1);
                break;

  case TDPREFIX:
                selectst(handle,TDPREFIX,data->fix.prefix^=1);
                break;

    case TDNOPW:
                selectst(handle,TDNOPW,data->fix.nopw^=1);
                break;

  case TDCONFIG:
                selectst(handle,TDCONFIG,data->fix.config^=1);
                break;

  case TDCANCEL:
      case TDOK:
                closeentry(i,icon==TDOK);
                break;

 }
}



void entryicon(void)
{
 entryiconsub(sentry);
}





              /* N L R D U */

static char entryiclst[10][5]=
{
     TDNAME,          0,           0,    TDNUMBER,            0,
   TDNUMBER,          0,           0,       TDPRE,       TDNAME,
      TDPRE,          0,           0,   TDPROMPT1,     TDNUMBER,
  TDPROMPT1,          0,           0,   TDPROMPT2,        TDPRE,
  TDPROMPT2,          0,           0,   TDPROMPT3,    TDPROMPT1,
  TDPROMPT3,          0,           0,    TDREPLY1,    TDPROMPT2,
   TDREPLY1,          0,           0,    TDREPLY2,    TDPROMPT3,
   TDREPLY2,          0,           0,    TDREPLY3,     TDREPLY1,
   TDREPLY3,          0,           0,   TDCOMMENT,     TDREPLY2,
  TDCOMMENT,          0,           0,           0,     TDREPLY3
};




void entrykeysub(int i,int * key)
{
 int cicon;
 int j;
 int ch;

 ch=*key;

 switch(ch)
 {
       case 27:
               closeentry(i,ch==CR);
               break;

       case CR:
               ch=CDOWN;

    case 0x18E:
    case 0x18F:
    case 0x19C:
    case 0x19D:
    case 0x19E:
    case 0x19F:
               ch&=0x18F;
               for(j=0;j<11;j++) if(entryiclst[j][0]==icon) break;
               cicon=entryiclst[j][(ch-0x18B)];
               if(cicon) iecarrot(ewind[i].handle,cicon);
               break;

    default:return;
 }
 *key=-1;
}





void entrykey(int * key)
{
 entrykeysub(sentry,key);
}




static void openentrywindow(int i)
{
 int     handle;
 entry * ep=&ewind[i];

 handle=createwindow(ENTRY);
 whandle[ENTRY]=0;

 ep->handle=handle;
 ep->inuse=1;

 setindirect(handle,TDNUMBER,25,ep->fix.number);
 setindirect(handle,TDCOMMENT,33,ep->comment);
 setindirect(handle,TDPRE,25,ep->predial);
 setindirect(handle,TDPROMPT1,25,ep->prompt[0]);
 setindirect(handle,TDPROMPT2,25,ep->prompt[1]);
 setindirect(handle,TDPROMPT3,25,ep->prompt[2]);
 setindirect(handle,TDREPLY1,25,ep->response[0]);
 setindirect(handle,TDREPLY2,25,ep->response[1]);
 setindirect(handle,TDREPLY3,25,ep->response[2]);
 setindirect(handle,TDNAME,13,ep->fix.name);
 setindirect(handle,TDPRES1,16,ep->scriptlabel[0]);
 setindirect(handle,TDPRES2,16,ep->scriptlabel[1]);
 setindirect(handle,TDPOSTS1,16,ep->scriptlabel[2]);
 setindirect(handle,TDPOSTS2,16,ep->scriptlabel[3]);

 selectst(handle,TDCONFIG,ep->fix.config);
 selectst(handle,TDNOPW,ep->fix.nopw);
 selectst(handle,TDPREFIX,ep->fix.prefix);
 selectst(handle,TDSHOW,ep->fix.show);

 writeicon(handle,TDPRES1,leaf(ep->script[0]));
 writeicon(handle,TDPRES2,leaf(ep->script[1]));
 writeicon(handle,TDPOSTS1,leaf(ep->script[2]));
 writeicon(handle,TDPOSTS2,leaf(ep->script[3]));

 writestatus(i);

 popup(handle,i);
 iecarrot(handle,TDNAME);
}



/* zeros fields to new values */

static void blankentry(int i)
{
 entry * ep;

 ep=&ewind[i];

 memset(ep,0,sizeof(entry));

 ep->fix.term=defaultterminal;
 ep->fix.data=databits;
 ep->fix.stop=stopbits;
 ep->fix.parity=paritybits;
 ep->fix.txrate=txbitrate;
 ep->fix.rxrate=rxbitrate;
 ep->fix.show=ep->fix.prefix=1;
 ep->fix.config=1;
}



static void newentry(void)
{
 int i;
 int k;
 int n;

 n=selected(&k);
 i=getentryhandle();
 if(i<0) return;
 if(n!=1) blankentry(i);
 else
 {
  dirtoentry(i,k);
  *ewind[i].fix.name=0;
  *ewind[i].oldname=0;
  *ewind[i].fix.number=0;
 }

 openentrywindow(i);
}


static void editentry(void)
{
 int i;
 int k;
 int n;

 n=selected(&k);
 i=getentryhandle();
 if(i<0) return;
 dirtoentry(i,k);
 openentrywindow(i);
}


/*****************************************************************************/
/* code for handling macro windows */

/* redraw directory */

void dirredrawsub(wimp_redrawstr * redrawstr,int more)
{
 int oy;
 int ox;
 int ylo;
 int yhi;
 int n;
 int y;
 int yp;
 int xp;
 int col;
 char string[32];
 fixedbit * fixp;

 wimpfontstart();

 while(more)
 {
  ox=redrawstr->box.x0-redrawstr->scx;
  oy=redrawstr->box.y1-redrawstr->scy;

  ylo=-(redrawstr->g.y1-oy);            /* small number, top of window */
  yhi=-(redrawstr->g.y0-oy);            /* bigger, bottom of window */

  n=(ylo-TDIRFY)/TDIRDY;
  if(n<0) n=0;
  y=n*TDIRDY+TDIRFY;

  while(y<=yhi && n<dirtot)
  {
   yp=oy-y-4;
   xp=ox+4;

   fixp=&dir[n].fix;

   if(fixp->selected) col=(6<<8)|0;
   else               col=(1<<8)|7;

   plinthtext(ox+TDIRFX,yp,TDIRFXW,TDIRHY,fixp->name,col);
   plinthtext(ox+TDIRSX,yp,TDIRSXW,TDIRHY,fixp->number,col);

   if(fixp->config)
   {
    plinthtext(ox+TDIRTX,yp,TDIRTXW,TDIRHY,
                            transtoken(termname[fixp->term]),col);

    strcpy(string,bitratemaps[bitrate2int(dir[n].fix.txrate)]);
    strcat(string,"/");
    strcat(string,bitratemaps[bitrate2int(dir[n].fix.rxrate)]);

    plinthtext(ox+TDIRGX,yp,TDIRGXW,TDIRHY,string,col);

    string[0]=dir[n].fix.data+'0';
    string[1]="NEOMS"[dir[n].fix.parity];
    string[2]=dir[n].fix.stop+'0';
    string[3]=0;

    plinthtext(ox+TDIRHX,yp,TDIRHXW,TDIRHY,string,col);
   }
   else
   {
    plinthtext(ox+TDIRTX,yp,TDIRTXW,TDIRHY,"",col);
    plinthtext(ox+TDIRGX,yp,TDIRGXW,TDIRHY,"",col);
    plinthtext(ox+TDIRHX,yp,TDIRHXW,TDIRHY,"",col);
   }

   y+=TDIRDY;
   n++;
  } 

  wimp_get_rectangle(redrawstr,&more);
 }

 wimpfontend();
}



void dirredraw(void)
{
 wimp_redrawstr redrawstr;
 int            more;
 redrawstr.w=ewindow;
 wimp_redraw_wind(&redrawstr,&more);
 dirredrawsub(&redrawstr,more);
}





void refreshdirentry(int n)
{
 wimp_redrawstr r;
 int handle=whandle[TDIR];
 int more;

 r.box.x0=0;
 r.box.x1=TDIRWIDTH;
 r.box.y1=-TDIRFY-n*TDIRDY;
 r.box.y0=r.box.y1-TDIRHY-4;
 r.w=handle;

 wimp_update_wind(&r,&more);
 dirredrawsub(&r,more);
}



/* scroll window to show entry */

static void displayentry(int n)
{
 int yhi=-TDIRFY-n*TDIRDY;
 int ylo=-TDIRFY-(n+1)*TDIRDY;
 int wlo;
 int whi;

 getw(whandle[TDIR]);

 whi=by-y1;
 wlo=by-y0;

 if(ylo<wlo || yhi>whi)
   openatscroll(whandle[TDIR],0,-TDIRFY-n*TDIRDY-TDIRDY/2+(y1-y0)/2);
}




void dirclose(void)
{
 int i;

 for(i=0;i<MAXEW;i++) if(ewind[i].inuse) entryclosesub(i);

 closedownt(TDIR);
 if(dirmodded) savedir();
}





static void dirclearselect(void)
{
 int i;
 for(i=0;i<dirtot;i++)
 {
  if(dir[i].fix.selected)
  {
   dir[i].fix.selected=0;
   refreshdirentry(i);
  }
 }
}




static void deleteselection(void)
{
 int i;
 int k;
 int n;

 n=selected(&k);

 if(n)
 {
  if(confirm(CONDC,"%s {TDIR04}",n==1?"{TDIR03}":"{TDIR02}")==1)
  {
   for(i=0;i<dirtot;i++)
   {
    if(dir[i].fix.selected) deleteentry(i--);
   }
  }
 }

 dirextent();
 reopenw(whandle[TDIR]);
 refreshwindow(whandle[TDIR]);
}




int dircmp(const void * p1,const void * p2)
{
 return(strcmp(((entry*)p1)->fix.name,((entry*)p2)->fix.name));
}



/* sort dir entries on name */

static void dirsort(void)
{
 qsort(dir,dirtot,sizeof(packedentry),dircmp);
 refreshwindow(whandle[TDIR]);
}


static void dirsearch(void)
{
 char   lstring[SYSNAMELEN];
 char * p=menuaddr(tdirsearch_menu,0);
 char * q=lstring;
 char * estring;
 int    entry;
 int    length;
 int    i;
 int    j;
 int    from;
 int    n;

 n=selected(&from);
 if(!n) from=0;
 else   from++;

 dirclearselect();

 while((*q++=toupper(*p++))!=0);

 for(entry=from;entry<dirtot;entry++)
 { 
  estring=dir[entry].fix.name;
  if((length=strlen(estring))==0) continue;

  for(i=0,j=0;i<=length;i++)
  {
   if((lstring[j]=='*') && (lstring[j+1]!=0) && i==length) break;
   else
   if(lstring[j]==toupper(estring[i])) j++;
   else
   if(lstring[j]=='*')
   {
    if(lstring[j+1]==toupper(estring[i])) j+=2;
   }
   else
    break;
  }

  if(i==(length+1)) break;
 }

 if(entry<dirtot)
 {
  dir[entry].fix.selected=1;
  refreshdirentry(entry);
  displayentry(entry);
 }
}




/* click on dir window */

void diricon(void)
{
 int n;
 int goodn;
 int k;
 int ns;

 getw(whandle[TDIR]);

 n=(by-mousey-TDIRFY)/TDIRDY;
 goodn=(n>=0 && n<dirtot);

 if(buttons==2)
 {
  if(goodn)
  {
   ns=selected(&k);

   if(ns==1) 
   {
    if(dir[k].fix.selected==2 && k!=n)
    {
     dir[k].fix.selected=0;
     refreshdirentry(k);
     ns=0;
    }
   }
   if(!ns)
   {
    dir[n].fix.selected=2;
    refreshdirentry(n);
   }
  }
  dirpop();
 }
 else
 {
  if(buttons==0x100 && goodn)   /* adjust */
  {
   if(dir[n].fix.selected) dir[n].fix.selected=0;
   else                dir[n].fix.selected=1;
   refreshdirentry(n);
  }
  else
  if(buttons==0x400 || buttons==0x1 || buttons==0x4)   /* select + */
  {
   dirclearselect();
   if(goodn)
   {
    dir[n].fix.selected=1;

   /* dprintf(0,"offset=%d",dir[n].offset); */


    refreshdirentry(n);
   }
   if(buttons==0x1)   /* double click with adjust */
   {
    editentry();
   }
   else
   if(buttons==0x4)   /* double click with select */ 
   {
    dodialselected();
   }
  }
 }
}




/* click on menu, display macro window */

void dirshow(void)
{
 int handle;

 if(getpassword()!=1) return;

 handle=createwindow(TDIR);
 if(!handle) return;
 dirextent();
 popup(whandle[TDIR],0);
}




/* boots up phone directory */

void dirboot(void)
{
 int i;

 dirtot=0;
 flex_alloc((flex_ptr)&dir,0);
 flex_alloc((flex_ptr)&dirs,0);

 entrywindows=0;
 for(i=0;i<MAXEW;i++) ewind[i].inuse=0;

 loaddir();
}


/*****************************************************************************/
/* call placing logic */

int prefix;

int redial;
int attempts;
int ardelay;

int attemptstillnow;
int artime;



/* stops all dialling */

void cleardial(void)
{
 int i;
 for(i=0;i<dirtot;i++) dir[i].fix.call=dir[i].fix.lastcall=0;
 remzeroevent(CALLZERO);
}




entry *  dialgetentry(int i)
{
 int handle;

 handle=getentryhandle();
 if(handle==-1) closeentry(MAXEW-1,0);
 handle=getentryhandle();

 ewind[handle].inuse=1;

 dirtoentry(handle,i);

 return(&ewind[handle]);
}



int dirrunscript(entry * ep,int i)
{
 int code;

 if(strlen(ep->script[i])) code=runscript(ep->script[i],1);
 else                      code=1;

 return(code);
}



/* things to do before dialling */
/* set baud rates               */
/* exec files                   */


int dirprelim(int i)
{
 entry * ep;
 int     code;

 ep=dialgetentry(i);

 if(ep->fix.config)
 {
  settxrate(ep->fix.txrate);
  setrxrate(ep->fix.rxrate);
  setparitybits(ep->fix.parity);
  setdatabits(ep->fix.data);
  setstopbits(ep->fix.stop);
 }

 if(strlen(ep->predial)) convertstringline(ep->predial);

          code=dirrunscript(ep,0);
 if(code) code=dirrunscript(ep,1);

 ep->inuse=0;

 return(code);
}



/* things to do after */
/* pop up terminal    */
/* do prompt response */
/* exec files         */


void dirpostit(int n)
{
 entry * ep;
 int     i;

 ep=dialgetentry(n);

 if(ep->fix.config) setterm(ep->fix.term);

 for(i=0;i<3;i++)
 {
  if(strlen(ep->prompt[i])) 
  {
   if(!getprompt(ep->prompt[i],2000))
   {
    break;
   }
  }

  if(strlen(ep->response[i]) && convertstringline(ep->response[i])) break;
 }

 if(dirrunscript(ep,2)) dirrunscript(ep,3);

 ep->inuse=0;
}






void callzerosub(void)
{
 int i;
 int ncall;

 if(zerotime>artime)
 {
  ncall=0;
  for(i=0;i<dirtot;i++) if(dir[i].fix.call) ncall++;

  if(!ncall) cleardial();
  else
  for(i=0;i<dirtot;i++)
  {
   if(dir[i].fix.call)            /* OK call this one       */
   {
    if(ncall>1 || !attemptstillnow)  /* gotta do prelim setups */
    {
     if(!dirprelim(i))
     {
      cleardial();
      return;
     }
    }

    band=dir[i].fix.band;
    strcpy(sysname,dir[i].fix.name);

    if(strlen(dir[i].fix.number))
    {
     if(!diallo(dir[i].fix.number,dir[i].fix.prefix))    /* dial number */
     {
      cleardial();
      return;
     }
    }

    if(online)
    {
     cleardial();
    /* writetolog("Online to %s.\n",dir[i].fix.name); */
     dirpostit(i);
     break;
    }
   }
  }
                       /* nb use clock below because of time taken to dial */

  if(!redial || ((++attemptstillnow)>=attempts)) cleardial();
  else                                           artime=clock()+ardelay;
 }
}


void callzero(void)
{
 static lockcall=0;

 if(lockcall) return;
 lockcall=1;
 callzerosub();
 lockcall=0;
}


/* commences the dial cycle */

void startdialcycle(void)
{
 int i;

 if(!unlocked)
 {
  for(i=0;i<dirtot;i++)
  {
   if(dir[i].fix.call && !dir[i].fix.nopw)
   {
    if(getpassword()!=1)
    {
     cleardial();
     return;
    }
    else
     break;
   }
  }
 }

 attemptstillnow=0;
 artime=0;
 addzeroevent(CALLZERO);
}


/* starts up dial cycle on any selected entries */

void dodialselected(void)
{
 int i;
 cleardial();

 for(i=0;i<dirtot;i++)
  if(dir[i].fix.selected) dir[i].fix.call=1;

 startdialcycle();
}


/* starts up dial cycle on one entry */

void dodialentry(int n)
{
 cleardial();
 dir[n].fix.call=1;
 startdialcycle();
}


/*****************************************************************************/

static void setpopdir(void)
{
 int n;
 int k;

 n=selected(&k);

 sprintf(menuaddr(tdir_menu,0),transtoken("TDIR09"),n==1?dir[k].fix.name:"");
 sprintf(menuaddr(tdir_menu,1),transtoken("TDIR06"),n==1?dir[k].fix.name:"");

 if(n>1)
  strcpy(menuaddr(tdir_menu,2),transtoken("TDIR07"));
 else
  sprintf(menuaddr(tdir_menu,2),transtoken("TDIR08"),n==1?dir[k].fix.name:"");

 unshadest(tdir_menu,0,n==1);
 unshadest(tdir_menu,1,n==1);
 unshadest(tdir_menu,2,n!=0);
 unshadest(tdir_menu,4,dirtot);
 unshadest(tdir_menu,5,dirtot);
 unshadest(tdir_menu,6,n>1);
 unshadest(tdir_menu,7,n);

 scrunch(0);
 strcpy(menuaddr(tdirprefix_menu,0),prefixstring);
 scrunch(1);
 strcpy(menuaddr(tdirpassword_menu,0),passwordstring);

}



void decodedir(int m2,int m3,int m4,int m5)
{

 switch(m2)
 {
  case 0: /* Dial */
         dodialselected();
         break;

  case 1: /* Edit */
         editentry();
         break;

  case 2: /* Delete */
         deleteselection();
         break;

  case 3: /* New entry */
         newentry();
         break;

  case 4: /* Sort */
         dirsort();
         break;

  case 5: /* Search */
         dirsearch();
         break;

  case 6: /* Cycle Dial */
         dodialselected();
         break;

  case 7: /* Clear selection */
         dirclearselect();
         break;

  case 8: /* password */
         changepassword(menuaddr(tdirpassword_menu,0));
     /*  memset(menuaddr(tdirpassword_menu,0),0,PASSWLEN); */
         moddir();
         break;

  case 9: /* prefix */
         strcpy(prefixstring,menuaddr(tdirprefix_menu,0));
         scrunch(1);
         memset(menuaddr(tdirprefix_menu,0),0,PREFIXLEN);
         moddir();
         break;


 }

 m3=m4=m5;
 setpopdir();
}



void dirpop(void)
{
 setpopdir();
 popmenu(tdir_menu);
}



/*****************************************************************************/

/* max size of ram buffer in bytes */

#define MYRMAX 0x1000

char * strings; /* points to area to store indirected menu entries in */

/* adds an (indirected) item to a menu */
/* return 1 if no room left */

int writemitem(int ** pr,char * string,int bits,int shade,int * maxl)
{
 int * smp=*pr;
 int   len;
 int   xlen;

 if((strings-(char*)smp)<24) return(1);

 smp[0]=bits;
 smp[1]=-1;
 smp[4]=0;

 if((unsigned int)string<0x8000)
 {
  len=(int)string;
  xlen=len+1;
  if(((strings-(char*)smp)-xlen)<24) return(1);
  strings-=xlen;
  memset(strings,0,xlen); 
  smp[2]=0x7000121 | shade;
  smp[3]=(int)strings;
  smp[5]=xlen;
 }
 else
 if((len=strlen(string))>12)
 {
  xlen=len+1;
  if(((strings-(char*)smp)-xlen)<24) return(1);
  strings-=xlen;
  strcpy(strings,string); 
  smp[2]=0x7000121 | shade;
  smp[3]=(int)strings;
  smp[5]=xlen;
 }
 else
 {
  len=strlen(string);
  smp[2]=0x7000021 | shade;
  smp[5]=0;
  smp[3]=0;
  strcpy((char*)(&smp[3]),string);
 }

 *pr+=6;
 if(*maxl<len) *maxl=len;
 return(0);
}


void writemheader(int * menup,char * title,int maxwidth)
{
 strcpy((char *)menup,title);
 *(((char *)menup)+12)=7;
 *(((char *)menup)+13)=2;
 *(((char *)menup)+14)=7;
 *(((char *)menup)+15)=0;
 menup[4]=12+16*maxwidth;
 menup[5]=40;
 menup[6]=0;
}


static menudecodefn menudecode;

void setrammenu(menudecodefn menudecoder)
{
 menudecode=menudecoder;

}


void decoderammenu(int m1)
{
 menudecode(m1);
}



char dialnamebuff[36];   /* used to retain last name/number dialled */

int setupentrymenu(void)
{ 
 int  * fontp;
 int  * fontm;
 int    i;
 int    maxwidth=0;

 fontm=(int*)myrambuff;

 usedrambuff=1;
 strings=myrambuff+MYRMAX;

 fontp=fontm+7;

 writemitem(&fontp,(char*)32,2+4,0,&maxwidth);
 maxwidth=12;

 i=0;
 while(i<dirtot)
 {
  if(dir[i].fix.show)
  {
   if(writemitem(&fontp,dir[i].fix.name,0,0,&maxwidth)) break;
   if((strings-(char*)fontp)<0x80) break; /* leave some room for ustyles */
  }
  i++;
 }
 *(fontp-6)|=0x80;

 writemheader(fontm,transtoken("TDIR10"),maxwidth); /* Fonts */

 mwpoint(comms_menu,0,(int)myrambuff);

 strcpy(menuaddr((int*)myrambuff,0),dialnamebuff);

 return((int)myrambuff);
}



int setupdrivermenu(void)
{
 int  * fontp;
 int  * fontm;
 int    maxwidth=0;
 fxstat f;

 fontm=(int*)myrambuff;

 usedrambuff=1;
 strings=myrambuff+MYRMAX;

 fontp=fontm+7;

 startscan();

 while(nextitem(path(DRVP),&f,NULL))
 {
  if(writemitem(&fontp,f.name,0,0,&maxwidth)) break;
  if((strings-(char*)fontp)<0x80) break; /* leave some room for ustyles */
 }
 *(fontp-6)|=0x80;

 writemheader(fontm,transtoken("TDIR11"),maxwidth); /* Fonts */

 return((int)myrambuff);
}

os_error * decodeusermacro(int m);

int setusermacros(void)
{
 int  * fontp;
 int  * fontm;
 int    maxwidth=0;
 int    i;

 setrammenu(decodeusermacro);

 fontm=(int*)myrambuff;

 usedrambuff=1;
 strings=myrambuff+MYRMAX;

 fontp=fontm+7;

 for(i=0;i<macrostot;i++)
 {
  if(macros[i].name[0]=='M' && macros[i].name[1]=='_')
  {
   writemitem(&fontp,macros[i].name+2,0,0,&maxwidth);
  }

  if((strings-(char*)fontp)<0x80) break; /* leave some room for ustyles */
 }

 *(fontp-6)|=0x80;

 writemheader(fontm,transtoken("TDIR12"),maxwidth); /* Fonts */

 mwpoint(misc_menu,6,(int)myrambuff);

 return((int)myrambuff);
}


int usermacros(void)
{
 int    i;
 int    n;

 n=0;

 for(i=0;i<macrostot;i++)
 {
  if(macros[i].name[0]=='M' && macros[i].name[1]=='_') n++;
 }

 return(n);
}


os_error * decodeusermacro(int m)
{
 int    i;
 int    n;

 n=-1;

 for(i=0;i<macrostot;i++)
 {
  if(macros[i].name[0]=='M' && macros[i].name[1]=='_')
  {
   n++;
   if(n==m) expandamacro(cedsendkey,i);
  }

  setusermacros();
 }

 return(NULL);
}



/*****************************************************************************/

static int tempprefix;
static int tempredial;


void dialoptionsicon(void)
{
 int handle=whandle[TDIALOPTIONS];
 int temp;

 switch(icon)
 {
  case 0:
         selectst(handle,0,tempprefix^=1);
         break;

  case 1:
         selectst(handle,1,tempredial^=1);
         break;

  case 7:
         prefix=tempprefix;
         redial=tempredial;
         if(geticonint(handle,3,&temp)) attempts=temp;
         if(geticonint(handle,5,&temp)) ardelay=temp*100;

         if(buttons==0x4) zapmenu();
         break;
 }
}





              /* N L R D U */

static char miscentryiclst[2][5]=
{
          3,          0,           0,           5,            0,
          5,          0,           0,           0,            3,
};



void dialoptionskey(int * key)
{
 int cicon;
 int j;
 int ch;

 ch=*key;

 switch(ch)
 {
       case 27:
               zapmenu();
               break;

       case CR:
               ch=CDOWN;

    case 0x18E:
    case 0x18F:
    case 0x19C:
    case 0x19D:
    case 0x19E:
    case 0x19F:
               ch&=0x18F;
               for(j=0;j<2;j++) if(miscentryiclst[j][0]==icon) break;
               cicon=miscentryiclst[j][(ch-0x18B)];
               if(cicon) iecarrot(whandle[TDIALOPTIONS],cicon);
               break;

    default:return;
 }
 *key=-1;
}




int setupdialoptions(void)
{
 int handle=createwindow(TDIALOPTIONS);

 tempprefix=prefix;
 tempredial=redial;

 writeiconf(handle,3,"%d",attempts);
 writeiconf(handle,5,"%d",ardelay/100);

 selectst(handle,0,tempprefix);
 selectst(handle,1,tempredial);

 return(handle);
}



#ifdef NEVER

void setpopcomms(void)
{
 tickst(comms_menu,1,redial);
 tickst(comms_menu,3,prefix);

 writemenuf(attempts_menu,0,"%d",attempts);
 writemenuf(delay_menu,0,"%d",ardelay/100);
}


void decoderedial(int m3,int m4)
{
 int temp;

 if(m4!=-1)
  switch(m3)
  {
   case 0:
          if(getmenuint(attempts_menu,0,&temp)) attempts=temp;
          break;

   case 1:
          if(getmenuint(delay_menu,0,&temp)) ardelay=temp*100;
          break;
  }
}


#endif





/* change this, look at next function */


void decodedial(int m3)
{
 char * p;
 int    n;
 int    i;

 if(m3==0 || m3<0)
 {
  /* if it is an int then dial it else set m3 to entry number */

  if(m3==0)
  {
   p=menuaddr((int*)myrambuff,0);
   while(isspace(*p)) p++;
   strcpy(dialnamebuff,p);
  }

  if(isdigit(*dialnamebuff)) dial(dialnamebuff);
  else
  if((n=findname(dialnamebuff))!=-1) dodialentry(n);
  else                               errorbox("{TDIR05}");
 }
 else
 if(m3>0 && m3<=dirtot)
 {
  /* dial entry */

  m3--;
  i=0;

  for(n=0;n<dirtot;n++)
  {
   if(dir[n].fix.show)
   {
    if(i==m3)
    {
     strcpy(dialnamebuff,dir[n].fix.name);
     dodialentry(n);
     break;
    }
    else
    {
     i++;
    }
   }
  }
 }
}

void diallast(void)
{
 decodedial(-1);
}


void decodecomms(int m2,int m3,int m4,int m5)
{
 switch(m2)
 {
  case 0:             /* Dial */
         decodedial(m3);
         break;

  case 2:             /* pop up directory */
         dirshow();
         break;

  case 3:             /* connect */
         connect();
         break;

  case 4:             /* disconnect */
         disconnect();
         break;

  case 5:             /* talk to modem */
         talk2modem();
         break;

  case 6:             /* reconnect */
         reconnect();
         break;

  case 7:             /* short break */
         shortbreak();
         break;

  case 8:             /* long break */
         longbreak();
         break;

 }
 m3=m4=m5;
}

